﻿using System.Dynamic;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace EasyXaf.DynamicForm.Blazor;

public class ExpandoObjectConverter : JsonConverter<ExpandoObject>
{
    private static object ProcessJsonElement(JsonElement element, JsonSerializerOptions options)
    {
        switch (element.ValueKind)
        {
            case JsonValueKind.Object:
                return JsonSerializer.Deserialize<ExpandoObject>(element.GetRawText(), options);

            case JsonValueKind.Array:
                var list = new List<object>();
                foreach (var item in element.EnumerateArray())
                {
                    list.Add(ProcessJsonElement(item, options));
                }
                return list;

            case JsonValueKind.String:
                return element.GetString();

            case JsonValueKind.Number:
                return GetNumberValue(element);

            case JsonValueKind.True:
            case JsonValueKind.False:
                return element.GetBoolean();

            case JsonValueKind.Null:
                return null;

            default:
                return element.ToString();
        }
    }

    private static object GetNumberValue(JsonElement element)
    {
        if (element.TryGetInt32(out var intValue))
        {
            return intValue;
        }

        if (element.TryGetInt64(out var longValue))
        {
            return longValue;
        }

        if (element.TryGetDouble(out var doubleValue))
        {
            return doubleValue;
        }

        if (element.TryGetDecimal(out var decimalValue))
        {
            return decimalValue;
        }

        return element.GetDouble();
    }

    public override ExpandoObject Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var expando = new ExpandoObject() as IDictionary<string, object>;
        using (var doc = JsonDocument.ParseValue(ref reader))
        {
            foreach (var element in doc.RootElement.EnumerateObject())
            {
                expando[element.Name] = ProcessJsonElement(element.Value, options);
            }
        }
        return expando as ExpandoObject;
    }

    public override void Write(Utf8JsonWriter writer, ExpandoObject value, JsonSerializerOptions options)
    {
        JsonSerializer.Serialize(writer, value, options);
    }
}
